#pragma once
#include <mutex>
// #include <mysql/mysql.h>

#include "util.hpp"
#include "log.hpp"

using namespace std;

class user_table
{
public:
	// 1.与Mysql的服务器进行交互的本质和同服务器进行交流的本质没有太多区别，所以我们在进行通信的
	// 时候依然需要使用对应的序列化反序列化来处理我们的用户信息
	user_table(const string &hostname,
			   const string &username,
			   const string &password,
			   const string &dbname,
			   uint16_t port = 3306)
	{
		_mysql = Mysql_util::mysql_create(hostname, username, password, dbname, port);
		assert(_mysql != NULL);
	}

	// 向数据库中新增一个用户，用户的格式类型为Json
	bool insert(Json::Value &user)
	{
// 为了成功的操作句柄并放入我们Json格式内部的数据，我们还需要格式化一个字符串出来才行
#define INSERT_USERS "insert user values(null,'%s',password('%s'),1000,0,0);"
		char sqlbuf[4096] = {0};
		if(user["username"].isNull()|| user["password"].isNull())
		{
			DLOG("密码或用户名不能为空！");
			return false;
		}


		sprintf(sqlbuf, INSERT_USERS, user["username"].asCString(), user["password"].asCString());

		int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
		if (ret == false)
		{
			DLOG("mysql inset user failed！");
			return false;
		}

		return true;
	}

	// 验证用户登录信息，以用户名和密码作为查询过滤条件，找到了就是有信息，找不到就返回一个错误
	bool login(Json::Value &user)
	{
#define LOGIN_USERS "select id ,score ,id,total_count,win_count from user where username ='%s' and password=password('%s')"
		char sqlbuf[4096] = {0};
		sprintf(sqlbuf, LOGIN_USERS, user["username"].asCString(), user["password"].asCString());
		MYSQL_RES *res = NULL;
		{
			unique_lock<mutex> lock(_mutex);

			int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
			if (ret == false)
			{
				DLOG("Login use mysql failed!");
				return false;
			}
			// 要么有数据要么没数据
			res = mysql_store_result(_mysql);
			if (res == NULL)
			{
				DLOG("can't find this users information!");
				return false;
			}
		}

		int row_num = mysql_num_rows(res);
		if (row_num != 1)
		{
			DLOG("the user informaiton queried is not unique!");
			return false;
		}
		MYSQL_ROW row = mysql_fetch_row(res);
		user["id"] = (Json::UInt64)stol(row[0]);
		user["score"] = (Json::UInt64)stol(row[1]);
		user["total_count"] = stoi(row[2]);
		user["win_count"] = stoi(row[3]);
		mysql_free_result(res);
		return true;
	}

	// 使用用户名来查找用户
	bool select_by_name(const string &name, Json::Value &user)
	{
#define SELECT_USERS "select id,score,total_count,win_count from user where username='%s'"
		char sqlbuf[4096] = {0};
		sprintf(sqlbuf, SELECT_USERS, name.c_str());

		MYSQL_RES *res = NULL;
		{
			unique_lock<mutex> lock(_mutex);

			int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
			if (ret == false)
			{
				DLOG("select_by_name use mysql failed!");
				return false;
			}
			res = mysql_store_result(_mysql);
			if (res == NULL)
			{
				DLOG("get user by name failed!");
				return false;
			}
		}
		int row_num = mysql_num_rows(res);
		if (row_num != 1)
		{
			DLOG("the user informaiton queried is not unique!");
			return false;
		}
		MYSQL_ROW row = mysql_fetch_row(res);
		user["id"] = (Json::UInt64)stol(row[0]);
		user["username"] = name;
		user["score"] = (Json::UInt64)stol(row[1]);
		user["total_count"] = stoi(row[2]);
		user["win_count"] =stoi(row[3]);
		mysql_free_result(res);
		return true;
	}

	bool select_by_id(uint64_t &id, Json::Value &user)
	{
#define SELECTID_USERS "select username,score,total_count,win_count from user where id=%d"
		char sqlbuf[4096] = {0};
		sprintf(sqlbuf, SELECTID_USERS, id);

		MYSQL_RES *res = NULL;
		{
			unique_lock<mutex> lock(_mutex);

			int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
			if (ret == false)
			{
				DLOG("select_by_id use mysql failed!");
				return false;
			}
			res = mysql_store_result(_mysql);
			if (res == NULL)
			{
				DLOG("get user by ID failed!");
				return false;
			}
		}
		int row_num = mysql_num_rows(res);
		if (row_num != 1)
		{
			DLOG("the user informaiton queried is not unique!");
			return false;
		}
		MYSQL_ROW row = mysql_fetch_row(res);
		user["id"] =  (Json::UInt64) id;
		user["username"] = row[0];
		user["score"] = (Json::UInt64)stol(row[1]);
		user["total_count"] = (Json::UInt64) stoi(row[2]);
		user["win_count"] = (Json::UInt64) stoi(row[3]);
		mysql_free_result(res);
		return true;
	}

	bool win(uint64_t id)
	{
		DLOG("判赢函数生效！生效者ID：%d ",id);

#define USER_WIN "update user set score=score+50,total_count=total_count+1,win_count=win_count+1 where id =%d;"
		char sqlbuf[4096] = {0};
		sprintf(sqlbuf, USER_WIN, id);
		int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
		if (ret == false)
		{
			DLOG("win use mysql failed!");
			return false;
		}
		return true;
	}

	bool lose(uint64_t id)
	{
		DLOG("判输函数生效！生效者ID：%d ",id);

#define USER_LOSE "update user set score=score-30,total_count=total_count+1,win_count=win_count where id =%d;"
		char sqlbuf[4096] = {0};
		sprintf(sqlbuf, USER_LOSE, id);
		int ret = Mysql_util::Mysql_exec(_mysql, sqlbuf);
		if (ret == false)
		{
			DLOG("lose use mysql failed!");
			return false;
		}
		return true;
	}
	~user_table()
	{
		Mysql_util::Mysql_destroy(_mysql);
		_mysql = NULL;
	}

	// 数据管理模块类的实现
	// 首先我们其实管理的是数据库内部的数据，所以我们必须使用Mysql句柄来控制
	// 所以我们的成员变量中就需要对应的句柄对象。
private:
	MYSQL *_mysql;
	// 虽然Mysql的语句执行的时候是线程安全的，但是加在一块用就不一定是了，所以我们还需要一把锁
	mutex _mutex;
};